Add a helper to make case-insensitive globs
authorMatthias Clasen <mclasen@redhat.com>
Fri, 4 Jun 2021 03:50:09 +0000 (23:50 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Sat, 5 Jun 2021 01:10:01 +0000 (21:10 -0400)
This will be used in GtkFileFilter in the future.

Tests included.

gtk/fnmatch.c
gtk/gtkprivate.h
testsuite/gtk/fnmatch.c

index 0ad1cd89929f872ab9b80c4a3c38851f9751a37d..9b51df208dc874b6b2a90cd2b6b88f3548e972e8 100644 (file)
@@ -253,3 +253,67 @@ _gtk_fnmatch (const char *pattern,
 {
   return gtk_fnmatch_intern (pattern, string, TRUE, no_leading_period, casefold);
 }
+
+/* Turn a glob pattern into a case-insensitive one, by replacing
+ * alphabetic characters by [xX] ranges.
+ */
+char *
+_gtk_make_ci_glob_pattern (const char *pattern)
+{
+  GString *s;
+  gboolean in_range = FALSE;
+
+  s = g_string_new ("");
+  for (const char *p = pattern; *p; p = g_utf8_next_char (p))
+    {
+      gunichar c = g_utf8_get_char (p);
+      if (in_range)
+        {
+          g_string_append_unichar (s, c);
+          if (c == ']')
+            in_range = FALSE;
+          continue;
+        }
+
+#if DO_ESCAPE
+      if (c == '\\')
+        {
+          g_string_append (s, "\\");
+          p = g_utf8_next_char (p);
+          if (*p == '\0')
+            break;
+
+          c = g_utf8_get_char (p);
+          g_string_append_unichar (s, c);
+          continue;
+        }
+#endif
+
+      if (c == '[')
+        {
+          g_string_append (s, "[");
+          p = g_utf8_next_char (p);
+          if (*p == '\0')
+            break;
+
+          c = g_utf8_get_char (p);
+          g_string_append_unichar (s, c);
+
+          in_range = TRUE;
+          continue;
+        }
+      else if (g_unichar_isalpha (c))
+        {
+          g_string_append (s, "[");
+          g_string_append_unichar (s, g_unichar_tolower (c));
+          g_string_append_unichar (s, g_unichar_toupper (c));
+          g_string_append (s, "]");
+        }
+      else
+        {
+          g_string_append_unichar (s, c);
+        }
+    }
+
+  return g_string_free (s, FALSE);
+}
index d24c2720653b99f18fc41356c9b187f2cdb839a0..8cb11c761caac2d74e9c71b97522233543d28f4c 100644 (file)
@@ -64,6 +64,9 @@ gboolean      _gtk_fnmatch                (const char *pattern,
                                            gboolean    no_leading_period,
                                            gboolean    casefold);
 
+char *        _gtk_make_ci_glob_pattern   (const char *pattern);
+
+
 char        * _gtk_get_lc_ctype           (void);
 
 void          _gtk_ensure_resources       (void);
index a614c1f99b067eab77a2801717b329ae8228e46b..e16bdae016bc269ffb304e7a895a79d439398c7c 100644 (file)
@@ -125,6 +125,37 @@ test_fnmatch (gconstpointer data)
   g_assert_true (_gtk_fnmatch (test->pat, test->str, test->no_leading_period, test->ci) == test->result);
 }
 
+typedef struct {
+  const char *glob;
+  const char *ci;
+} CITest;
+
+static CITest citests[] = {
+  { "*.txt", "*.[tT][xX][tT]" },
+  { "*.TXT", "*.[tT][xX][tT]" },
+  { "*?[]-abc]t", "*?[]-abc][tT]" },
+#ifdef DO_ESCAPE
+  /* Tests of escaping */
+  { "\\\\", "\\\\" },
+  { "\\??", "\\??" },
+  { "\\**", "\\**" },
+  { "\\[", "\\[" },
+  { "\\[a-", "\\[[aA]-" },
+  { "\\[]", "\\[]" },
+#endif
+};
+
+static void
+test_ci_glob (gconstpointer data)
+{
+  const CITest *test = data;
+  char *ci;
+
+  ci = _gtk_make_ci_glob_pattern (test->glob);
+  g_assert_cmpstr (ci, ==, test->ci);
+  g_free (ci);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -136,5 +167,11 @@ main (int argc, char *argv[])
       g_test_add_data_func (path, &tests[i], test_fnmatch);
     }
 
+  for (int i = 0; i < G_N_ELEMENTS (citests); i++)
+    {
+      char *path = g_strdup_printf ("/ci-glob/test%d", i);
+      g_test_add_data_func (path, &citests[i], test_ci_glob);
+    }
+
   return g_test_run ();
 }